using System;
using System.Xml.XPath;
using AntEater.ComponentModel.BuildElements;
using System.Collections.Generic;
using System.IO;
using AntEater.ComponentModel;

namespace AntEater.Runtime.Services.Transformation
{
    /// <summary>
    /// This Service Transforms an MSBuild Xml File into the AntEater Build Element
    /// Component Model.
    /// </summary>
    public class XmlToBuildElementTransformationService : RuntimeServiceBase, IXmlToBuildElementTransformationService
    {

        /// <summary>
        /// Gets the build engine path.
        /// </summary>
        /// <value>The build engine path.</value>
        public string BuildEnginePath {
            get { return this.Context.Options.BuildEnginePath; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is reusable.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is reusable; otherwise, <c>false</c>.
        /// </value>
        public override bool IsReusable {
            get {
                return false;
            }
        }

        /// <summary>
        /// Transforms the specified source.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="buildFilePath">The build file path.</param>
        /// <returns></returns>
        public XmlToBuildElementTransformationResult Transform(Stream source, string buildFilePath) {

            XmlToBuildElementTransformationResult result = null;

            TransformerContext transformerContext = new TransformerContext(this, buildFilePath);

            BuildFileDefinition buildFileDefinition = new BuildFileDefinition(buildFilePath);
            
            List<TransformationError> errors = new List<TransformationError>();
            XPathDocument document = new XPathDocument(source);
            XPathNavigator navigator = document.CreateNavigator();

            buildFileDefinition.RootBuildElement.ChildElements.AddRange(this.TransformBuildFile(navigator, errors, null, buildFileDefinition, transformerContext));

            result = new XmlToBuildElementTransformationResult(buildFileDefinition, errors);

            return result;
        }

        /// <summary>
        /// Transforms the build file.
        /// </summary>
        /// <param name="navigator">The navigator.</param>
        /// <param name="errors">The errors.</param>
        /// <param name="parentElement">The parent element.</param>
        /// <param name="buildFileDefinition">The build file definition.</param>
        /// <param name="transformerContext">The transformer context.</param>
        /// <returns></returns>
        internal List<IBuildElement> TransformBuildFile(XPathNavigator navigator, List<TransformationError> errors, IBuildElement parentElement, BuildFileDefinition buildFileDefinition, TransformerContext transformerContext) {
            return this.ParseNodeLevel(navigator, errors, parentElement, buildFileDefinition, null, transformerContext);
        }

        /// <summary>
        /// Parses the node level.
        /// </summary>
        /// <param name="navigator">The navigator.</param>
        /// <param name="errors">The errors.</param>
        /// <param name="parentElement">The parent element.</param>
        /// <param name="buildFileDefinition">The build file definition.</param>
        /// <param name="parentTransformerType">Type of the parent transformer.</param>
        /// <param name="transformerContext">The transformer context.</param>
        /// <returns></returns>
        private List<IBuildElement> ParseNodeLevel(XPathNavigator navigator, List<TransformationError> errors, IBuildElement parentElement, BuildFileDefinition buildFileDefinition, Type parentTransformerType, TransformerContext transformerContext) {
            IBuildElement element;
            List<IBuildElement> elements = new List<IBuildElement>();

            if (navigator.MoveToFirstChild()) {
                do {
                    IBuildElementTransformer transformer;

                    transformer = BuildElementTransformerFactory.GetTransformer(navigator.Name, navigator.NodeType, parentTransformerType, transformerContext);

                    if (transformer != null) {
                        element = transformer.GetBuildElement(navigator.Clone(), buildFileDefinition, errors);

                        if (element != null) {
                            if (parentElement != null) {
                                IContainerBuildElement container = parentElement as IContainerBuildElement;

                                if (container != null) {
                                    container.ChildElements.Add(element);
                                }
                            }

                            elements.Add(element);
                            ParseNodeLevel(navigator.Clone(), errors, element, buildFileDefinition, transformer.GetType(), transformerContext);
                        }
                    }


                } while (navigator.MoveToNext(XPathNodeType.All));
            }

            return elements;
        }
    }
}
